using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using Castle.Core;

namespace Brilliance.navigation
{
    public class Navigator : INavigator
    {
        private readonly IViewFactory viewFactory;
        private readonly IPresenterFactory presenterFactory;

        public Navigator(IViewFactory viewFactory, IPresenterFactory presenterFactory)
        {
            this.viewFactory = viewFactory;
            this.presenterFactory = presenterFactory;
        }

        private void OnNavigate(object sender, GenericEventArgs<INavigateAction> e)
        {
            var formerPresenter = sender as IPresenter;
            IPresenter[] presenters;
            if (formerPresenter != null && !string.IsNullOrEmpty(e.ResultName))
            {
                var navigateAction = ExtractNavigationInfo(formerPresenter, e);
                var request = ExtractRequestParameters(formerPresenter);
                presenters = navigateAction.Navigate(viewFactory, presenterFactory, request);
            }
            else
            {
                presenters = e.Result.Navigate(viewFactory, presenterFactory, null);
            }
            WireUpPresenters(presenters);
        }

        private INavigateAction ExtractNavigationInfo(IPresenter presenter, GenericEventArgs<INavigateAction> args)
        {
            var inf = presenter.GetType();
            var attributes = inf.GetCustomAttributes(typeof (NavigationAttribute), false);
            var resultName = args.ResultName;
            var navigation = attributes.First(x => string.Equals(((NavigationAttribute) x).Qualifier, resultName));
            var action = (INavigateAction) Activator.CreateInstance(((NavigationAttribute) navigation).NavigationAction);

            return action;
        }

        private Request ExtractRequestParameters(IPresenter presenter)
        {
            var inf = presenter.GetType();
            var properties = inf.GetProperties();
            var outProperties = properties.Where(x => x.IsDefined(typeof (OutAttribute), true)).ToList();
            IDictionary<Type, Dictionary<string, object>> param = new Dictionary<Type, Dictionary<string, object>>();
            outProperties.ForEach(outProperty =>
                                      {
                                          var type = outProperty.PropertyType;
                                          if (!param.ContainsKey(type))
                                          {
                                              param[type] = new Dictionary<string, object>();
                                          }
                                          var attribute = (OutAttribute) outProperty.GetCustomAttributes(typeof (OutAttribute), true).First();
                                          param[type][attribute.Qualifier] = outProperty.GetValue(presenter, null);
                                      });
            return new Request(param);
        }

        private void WireUpPresenters(params IPresenter[] presenters)
        {
            presenters.ForEach(presenter =>
                                   {
                                       presenter.Navigate += OnNavigate;
                                       presenter.Display();
                                   });
        }

        public void Start(INavigateAction welcomeWindowAction)
        {
            OnNavigate(this, new GenericEventArgs<INavigateAction>(welcomeWindowAction));
        }

        public void Dispose()
        {
        }
    }
}